library(tidyverse)
library(janitor)
1 load & explore data
Load the housing_prices.csv data set and undertake an initial
exploration of the data. You will find details on the data set on the
relevant Kaggle
page
housing_prices <- read_csv("data/housing_prices.csv")
Rows: 19675 Columns: 10── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): ocean_proximity
dbl (9): longitude, latitude, housing_median_age, total_rooms, total_bedrooms, population, households, median_i...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
glimpse(housing_prices)
Rows: 19,675
Columns: 10
$ longitude <dbl> -122.23, -122.22, -122.24, -122.25, -122.25, -122.25, -122.25, -122.25, -122.26, -122.…
$ latitude <dbl> 37.88, 37.86, 37.85, 37.85, 37.85, 37.85, 37.84, 37.84, 37.84, 37.84, 37.85, 37.85, 37…
$ housing_median_age <dbl> 41, 21, 52, 52, 52, 52, 52, 52, 42, 52, 52, 52, 52, 52, 52, 50, 52, 52, 50, 52, 40, 42…
$ total_rooms <dbl> 880, 7099, 1467, 1274, 1627, 919, 2535, 3104, 2555, 3549, 2202, 3503, 2491, 696, 2643,…
$ total_bedrooms <dbl> 129, 1106, 190, 235, 280, 213, 489, 687, 665, 707, 434, 752, 474, 191, 626, 283, 347, …
$ population <dbl> 322, 2401, 496, 558, 565, 413, 1094, 1157, 1206, 1551, 910, 1504, 1098, 345, 1212, 697…
$ households <dbl> 126, 1138, 177, 219, 259, 193, 514, 647, 595, 714, 402, 734, 468, 174, 620, 264, 331, …
$ median_income <dbl> 8.3252, 8.3014, 7.2574, 5.6431, 3.8462, 4.0368, 3.6591, 3.1200, 2.0804, 3.6912, 3.2031…
$ median_house_value <dbl> 452600, 358500, 352100, 341300, 342200, 269700, 299200, 241400, 226700, 261100, 281500…
$ ocean_proximity <chr> "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "NEAR BAY", "N…
Dataset about areas - population, households, features of these. One
character column (ocean_proximity), rest are numeric: location, local
population size and number of households, number of rooms and bedrooms
in the population’s households, median age (of the population from 1990
census), median income, median house value.
housing_prices %>%
summarise(across(everything(),
~sum(is.na(.x))))
Only var with missing values is total_bedrooms, 200 NAs out of the
19,675 observations. Tbc how to deal with these, if at all.
2 check for explanatory vars that correlate
We expect the total_rooms of houses to be strongly correlated with
total_bedrooms. Use ggpairs() to investigate correlations between these
two variables.
It’s overkill to look at all the vars, so just look at the two of
interest:
housing_prices %>%
select(total_rooms, total_bedrooms) %>%
GGally::ggpairs()

They look strongly positively correlated, and corr = 0.934 so that
indicates a very strong positive correlation. (And *** indicate this is
statistically significant, i.e. we can reject the null hypothesis that
total_bedrooms does not affect total_rooms; it is very unlikely this
data is from a world where the null hypothesis is true.)
3 trim data
drop total_bedrooms from the dataset, and use only total_rooms going
forward
Because these variables are related, we should only use one of these
in our model.
housing_prices <- housing_prices %>%
select(-total_bedrooms)
total_bedrooms was the only var with missing values, so we also don’t
need to worry about these anymore.
4 explore numeric predictors
We are interested in developing a regression model for the
median_house_value of a house in terms of the possible predictor
variables in the dataset. i. Use ggpairs() to investigate correlations
between median_house_value and the predictors (this may take a while to
run, don’t worry, make coffee or something).
GGally::ggpairs(housing_prices)

From these, it looks like there are otherexplanatory variables that
are strongly related:
- population, household and total_rooms are all correlated - they
could be combined to create a derived variable as some kind of indicator
of housing supply/demand, e.g. households * total_rooms / population
(where > 1 is plenty of supply, < 1 is not enough supply, so some
demand pressure)
- long and lat are two parts to a location, and are strongly related -
they should be combined to indicate individual locations (or areas) in
California (where this data is about)
Taking these into account, and looking at the magnitude of the
correlation coefficients that are significant (*s), I would consider
including the following in a model, in order of steps:
- income
- location (lat, long) - look at lat below
- demand (something derived from pop, household, rooms) - look at
rooms below
- note: rooms is also an indicator of hoiuse size, could be useful on
its own
- age
- Perform further ggplot visualisations of any significant
correlations you find.
# income
housing_prices %>%
ggplot(aes(x = median_income, y = median_house_value)) +
geom_point(fill = "grey60", size = 0.5) +
geom_smooth(method = lm)

cor(housing_prices$median_house_value, housing_prices$median_income)
[1] 0.6426108
Interpret: There is a strong positive correlation
between median house value and median income of an area. 64% of
variation in house value is explained by variation in income.
This could be a useful explanatory variable.
Note also there seems to be an upper limit in median_house_value, odd
lack of values above 500,000.
housing_prices %>%
ggplot(aes(x = latitude, y = median_house_value)) +
geom_point(fill = "grey60") +
geom_smooth(method = lm)

This is not a linear relationship - ther appear to be clusters where
house price is more variable (higher peaks) - i.e. certain latitudes, I
suspect these are where cities are.
Do not use this as a variable on its own (consider using location
though - maybe even encoding as different areas?)
# total_rooms
housing_prices %>%
ggplot(aes(x = total_rooms, y = median_house_value)) +
geom_point(fill = "grey60") +
geom_smooth(method = lm)

This does not look homoeskedastic, lots of data points at lower
total_rooms, but with high degree of spread of house values. Suggest
total_rooms might need to be standardised by some factor?
# median_age
housing_prices %>%
ggplot(aes(x = housing_median_age, y = median_house_value)) +
geom_point(fill = "grey60") +
geom_smooth(method = lm)

No obvious correlation here. Do not use in model.
5 explore categorical predictor
Shortly we may try a regression model to fit the categorical
predictor ocean_proximity. Investigate the level of ocean_proximity
predictors. How many dummy variables do you expect to get from it?
housing_prices %>%
distinct(ocean_proximity)
There are 5 distinct categories in ocean_proximity, so expect to
generate 4 dummy variables. Set inland as the
reference, everything else gets more proximal to ocean.
housing_prices_trim <- housing_prices %>%
# set up dummy variables for ocean_proximity
fastDummies::dummy_cols(select_columns = "ocean_proximity",
remove_selected_columns = TRUE) %>%
janitor::clean_names() %>%
# set inland as reference
select(-ocean_proximity_inland)
# check for any potential correlations
housing_prices %>%
ggplot(aes(x = ocean_proximity, y = median_house_value)) +
geom_boxplot()

House prices on island look higher than the rest, this may be a
sensible predictor to use in a model.
Update: from boxplot, actually looks like 3
categories that are similar: inland, island, and proximal to ocean
(combining the 3 others) –> which would suggest 2 dummy variables
(with inland set as reference).
housing_prices %>%
summarise(count = n(), .by = ocean_proximity)
~20,000 observations of which, 8,600 are <1h to ocean, 5 are on
island, 2000 are near bay, 2446 are near ocean, rest are inland
(~6,500).
There are only 5 observations for island, so not enough data here to
model this category, and also dragging up house prices - suggest
dropping this category altogether for now.
So let’s use inland as reference, drop island observations, and
combine the rest into a predictor “ocean_proximal”:
housing_prices_ocean <- housing_prices %>%
filter(ocean_proximity != "ISLAND") %>%
mutate(ocean_proximal = if_else(
ocean_proximity == "INLAND",
FALSE, TRUE)) %>%
select(-ocean_proximity)
head(housing_prices_ocean)
6 simple linear regression with income
Start with simple linear regression. Regress median_house_value on
median_income and check the regression diagnostics.
model_income <- lm(formula = median_house_value ~ median_income, housing_prices_trim)

library(ggfortify)
autoplot(model_income)

Intrepret:
- RvF plot has a big wedge shape, which suggests it is heterskedastic
- we need to add another variable to explain the variation more
consistently across x values and generate a good linear model here
- QQ plot is not linear, the residuals are not normally distrubted,
worse at the extremes, look negatively skewed (right skewed) in the
histogram below
- Scale-Location - looks to be some potential outliers, points 11331
and 1755.?. (cannot expand plot to see)
- These points and another point (1565..?) have high leverage and look
to be influencing in the RvL plot

# inspect the high leverage / influencing points
housing_prices_trim %>%
ggplot(aes(x = median_income, y = median_house_value)) +
geom_point(fill = "grey60", size = 0.5) +
geom_smooth(method = lm) +
geom_text(aes(label = 1:nrow(housing_prices)), nudge_x = 0.5, nudge_y = 1, size = 2)

Points 11331, 17556 and 1555 appear to be the points of interest.
housing_prices[c(11331,17556,1555),]
Raw data doesn’t look suspect, not an obvious processing error. But
note the population looks tiny, very few households. Might want to
consider filtering for data where households is sizeable enough…
housing_prices %>%
ggplot(aes(x = population)) +
geom_histogram(bins = 100, colour = "white")

housing_prices %>%
ggplot(aes(x = households)) +
geom_histogram(bins = 100, colour = "white")

It doesn’t make sense to filter out low number households or
population, given the distributions here - they don’t seem to be
odd.
Try removing a couple of points:

Removing these values has not helped the model, it just shows even
more extreme values (high income, low house price). So do not remove
these points (continue with housing_prices_trim).
summary(model_income)
Call:
lm(formula = median_house_value ~ median_income, data = housing_prices_trim)
Residuals:
Min 1Q Median 3Q Max
-513966 -51564 -13979 36549 403935
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 45457.0 1359.0 33.45 <2e-16 ***
median_income 39987.0 339.9 117.64 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 74870 on 19673 degrees of freedom
Multiple R-squared: 0.4129, Adjusted R-squared: 0.4129
F-statistic: 1.384e+04 on 1 and 19673 DF, p-value: < 2.2e-16
Here, the model suggests a function:
median_house_value (in $) ~= ~40,000*median_income + 45,500
the p-value (<2.2e-16) is very small (much below a 0.05
significance threshold), which indicates it is highly unlikely this
non-zero coefficient would happen in a world where the null hypothesis
is true (that median_income has no affect on median_house_value), so we
can reject the null hypothesis and say there is enough evidence to
suggest that income does affect house value.
The standard error is sizeable (~USD 75,000) - we are talking about
house prices in the region of USD 300-500,000 with a an IQR of ~ 130
(the QQ range is ~ 120-250k in the boxplot below) so this error is half
the IQR so is quite large for this to be a good model.
housing_prices %>%
ggplot(aes(x = median_house_value)) +
geom_boxplot()

The R2 is 0.4129, which is an ok correlation for such a variable
outcome (house value).
Overall this simple linear model does not meet
several assumptions of linear regression, so we should not accept that
this model is a good fit for our data. We need to improve it - we can
try adding another predictor variable in…
7 add another var to model
Add another predictor of your choice. Check your assumptions,
diagnostics, and interpret the model.
7a + island
Let’s try with ocean proximity: island.
Considering it as an independent variable, but not using the other
ocean_proximities – not sure if this is a correct approach for
non-binary categorical variables…

Oh this has not improved the model…
- RvF - still got a wedge shape, so not linear
- QQ - residuals still not normally distributed
- S-L still got a wedge here too, so heterskedastic
- RvL - shows many points are near the centroid, a couple of points
are very far

This distribution doesn’t look so bad though - maybe it is shifted
left?
summary(model_income_island)
Call:
lm(formula = median_house_value ~ median_income + ocean_proximity_island,
data = housing_prices_trim)
Residuals:
Min 1Q Median 3Q Max
-514154 -51494 -13940 36548 404045
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 45320.1 1357.6 33.382 < 2e-16 ***
median_income 40008.7 339.6 117.828 < 2e-16 ***
ocean_proximity_island 225319.3 33449.9 6.736 1.67e-11 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 74780 on 19672 degrees of freedom
Multiple R-squared: 0.4143, Adjusted R-squared: 0.4142
F-statistic: 6958 on 2 and 19672 DF, p-value: < 2.2e-16
mosaic::plotModel(model_income_island)

I would expect to have seen parallel slopes here (one for island =
TRUE, another for island = FALSE) - but there is only one line.
housing_prices_trim %>%
filter(ocean_proximity_island == 1)
There are only 5 observations with island == TRUE so cannot use this
in our model!
Try again…
7b + house_size / occupancy
Let’s derive mean house_size using total_rooms/households
housing_prices_trim <- housing_prices_trim %>%
mutate(mean_house_size = total_rooms / households)
head(housing_prices_trim)
housing_prices_trim %>%
ggplot(aes(x = mean_house_size, y = median_house_value)) +
geom_point(size = 0.5)

This is a no-goer - looks like majority of houses are around the same
size, with a few extreme values…
What about demand, some kind of measure of households by population?
Derive avg_occupancy
housing_prices_trim <- housing_prices_trim %>%
select(-mean_house_size) %>%
mutate(avg_occupancy = population / households) # indicates # people per household
head(housing_prices_trim)
housing_prices_trim %>%
ggplot(aes(x = avg_occupancy, y = median_house_value)) +
geom_point(size = 0.5)

Now there are 7 points that look like extreme values… could we filter
for avg_occupancy < 100??
This may be over-wrangling the data, but these few values do not make
sense for a model looking at ~20,000 observations
nrow(housing_prices_trim)
[1] 19675
# > 100 filters 4 out
# > 50 filters 7 out
housing_prices_rm_high_occupancy <- housing_prices_trim %>%
filter(!avg_occupancy > 50)
housing_prices_rm_high_occupancy %>%
ggplot(aes(x = avg_occupancy, y = median_house_value)) +
geom_point(size = 0.5)

Now we can see scatter of data, but still does not look to have any
linear relationship. Could try scaling but I don’t think that is the
problem here…
7c + ocean_proximity
Using house_prices_ocean for our dummy variable
ocean_proximal:
housing_prices_ocean %>%
ggplot(aes(x = ocean_proximal, y = median_house_value)) +
geom_boxplot()

There looks to be an effect of ocean proximity with house prices,
where being close to the ocean has higher house prices, but there’s also
a long tail of values for FALSE. Let’s try including this in our model
as a second explanatory variable:
model_income_ocean <- lm(median_house_value ~ median_income + ocean_proximal,
housing_prices_ocean)

This gives us 2 parallel lines - it looks as though being close to
the ocean shifts median house_value higher.
Let’s diagnose this model:

- RvF - still a wedge shape, so not linear
- QQ plot does not suggest normally distributed at the extremes
- S-L - still a wedge shape so heterskedastic
- there may still be a couple of points that are influencing the model
here
So the model does not match our assumptions of linear regression.
Let’s look at the model anyway…
summary(model_income_ocean)
Call:
lm(formula = median_house_value ~ median_income + ocean_proximal,
data = housing_prices_ocean)
Residuals:
Min 1Q Median 3Q Max
-482054 -42181 -11818 27998 376321
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 12000.4 1268.3 9.462 <2e-16 ***
median_income 34888.2 305.4 114.237 <2e-16 ***
ocean_proximalTRUE 78026.8 1018.6 76.599 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 65630 on 19667 degrees of freedom
Multiple R-squared: 0.5485, Adjusted R-squared: 0.5485
F-statistic: 1.195e+04 on 2 and 19667 DF, p-value: < 2.2e-16
This model (if it were a good fit) suggests base median house value
is USD 12,000 and increases with median income (multiplier effect,
*35,000), and is also shifted up if the location is close to the ocean
(by USD 78,000).
The R2 indicates the model is a good fit, and the p-value is way
below a signficance threshold of 0.05, suggesting we can reject the null
hypothesis (that these explanatory variables do not predict house
value).
This model summary would look good, except that looking at autoplot
indicates the linear model is not appropriate here.
To try: standardise or scale the variables somehow? But this won’t
change the distribution of each (i.e. won’t shift the long tail of high
house prices or high income…)
LS0tCnRpdGxlOiAid2sxMGQyIGhvbWV3b3JrIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoamFuaXRvcikKYGBgCgojIDEgbG9hZCAmIGV4cGxvcmUgZGF0YQoKPiBMb2FkIHRoZSBob3VzaW5nX3ByaWNlcy5jc3YgZGF0YSBzZXQgYW5kIHVuZGVydGFrZSBhbiBpbml0aWFsIGV4cGxvcmF0aW9uIG9mIHRoZSBkYXRhLiBZb3Ugd2lsbCBmaW5kIGRldGFpbHMgb24gdGhlIGRhdGEgc2V0IG9uIHRoZSByZWxldmFudCBbS2FnZ2xlIHBhZ2VdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvY2FtbnVnZW50L2NhbGlmb3JuaWEtaG91c2luZy1wcmljZXMpIAoKYGBge3J9CmhvdXNpbmdfcHJpY2VzIDwtIHJlYWRfY3N2KCJkYXRhL2hvdXNpbmdfcHJpY2VzLmNzdiIpCmBgYAoKYGBge3J9CmdsaW1wc2UoaG91c2luZ19wcmljZXMpCmBgYAoKRGF0YXNldCBhYm91dCBhcmVhcyAtIHBvcHVsYXRpb24sIGhvdXNlaG9sZHMsIGZlYXR1cmVzIG9mIHRoZXNlLiBPbmUgY2hhcmFjdGVyIGNvbHVtbiAob2NlYW5fcHJveGltaXR5KSwgcmVzdCBhcmUgbnVtZXJpYzogbG9jYXRpb24sIGxvY2FsIHBvcHVsYXRpb24gc2l6ZSBhbmQgbnVtYmVyIG9mIGhvdXNlaG9sZHMsIG51bWJlciBvZiByb29tcyBhbmQgYmVkcm9vbXMgaW4gdGhlIHBvcHVsYXRpb24ncyBob3VzZWhvbGRzLCBtZWRpYW4gYWdlIChvZiB0aGUgcG9wdWxhdGlvbiBmcm9tIDE5OTAgY2Vuc3VzKSwgbWVkaWFuIGluY29tZSwgbWVkaWFuIGhvdXNlIHZhbHVlLgoKYGBge3J9CmhvdXNpbmdfcHJpY2VzICU+JSAKICBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgICAgICAgIH5zdW0oaXMubmEoLngpKSkpCmBgYAoKT25seSB2YXIgd2l0aCBtaXNzaW5nIHZhbHVlcyBpcyB0b3RhbF9iZWRyb29tcywgMjAwIE5BcyBvdXQgb2YgdGhlIDE5LDY3NSBvYnNlcnZhdGlvbnMuIFRiYyBob3cgdG8gZGVhbCB3aXRoIHRoZXNlLCBpZiBhdCBhbGwuCgojIDIgY2hlY2sgZm9yIGV4cGxhbmF0b3J5IHZhcnMgdGhhdCBjb3JyZWxhdGUKCj4gV2UgZXhwZWN0IHRoZSB0b3RhbF9yb29tcyBvZiBob3VzZXMgdG8gYmUgc3Ryb25nbHkgY29ycmVsYXRlZCB3aXRoIHRvdGFsX2JlZHJvb21zLiBVc2UgZ2dwYWlycygpIHRvIGludmVzdGlnYXRlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXMuCgpJdCdzIG92ZXJraWxsIHRvIGxvb2sgYXQgYWxsIHRoZSB2YXJzLCBzbyBqdXN0IGxvb2sgYXQgdGhlIHR3byBvZiBpbnRlcmVzdDoKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KaG91c2luZ19wcmljZXMgJT4lIAogIHNlbGVjdCh0b3RhbF9yb29tcywgdG90YWxfYmVkcm9vbXMpICU+JSAKICBHR2FsbHk6OmdncGFpcnMoKQpgYGAKClRoZXkgbG9vayBzdHJvbmdseSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQsIGFuZCBjb3JyID0gMC45MzQgc28gdGhhdCBpbmRpY2F0ZXMgYSB2ZXJ5IHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbi4gKEFuZCAqKiogaW5kaWNhdGUgdGhpcyBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LCBpLmUuIHdlIGNhbiByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRvdGFsX2JlZHJvb21zIGRvZXMgbm90IGFmZmVjdCB0b3RhbF9yb29tczsgaXQgaXMgdmVyeSB1bmxpa2VseSB0aGlzIGRhdGEgaXMgZnJvbSBhIHdvcmxkIHdoZXJlIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4pCgojIDMgdHJpbSBkYXRhCgo+IGRyb3AgdG90YWxfYmVkcm9vbXMgZnJvbSB0aGUgZGF0YXNldCwgYW5kIHVzZSBvbmx5IHRvdGFsX3Jvb21zIGdvaW5nIGZvcndhcmQKCkJlY2F1c2UgdGhlc2UgdmFyaWFibGVzIGFyZSByZWxhdGVkLCB3ZSBzaG91bGQgb25seSB1c2Ugb25lIG9mIHRoZXNlIGluIG91ciBtb2RlbC4KCmBgYHtyfQpob3VzaW5nX3ByaWNlcyA8LSBob3VzaW5nX3ByaWNlcyAlPiUgCiAgc2VsZWN0KC10b3RhbF9iZWRyb29tcykKYGBgCgp0b3RhbF9iZWRyb29tcyB3YXMgdGhlIG9ubHkgdmFyIHdpdGggbWlzc2luZyB2YWx1ZXMsIHNvIHdlIGFsc28gZG9uJ3QgbmVlZCB0byB3b3JyeSBhYm91dCB0aGVzZSBhbnltb3JlLgoKIyA0IGV4cGxvcmUgbnVtZXJpYyBwcmVkaWN0b3JzCgo+IFdlIGFyZSBpbnRlcmVzdGVkIGluIGRldmVsb3BpbmcgYSByZWdyZXNzaW9uIG1vZGVsIGZvciB0aGUgbWVkaWFuX2hvdXNlX3ZhbHVlIG9mIGEgaG91c2UgaW4gdGVybXMgb2YgdGhlIHBvc3NpYmxlIHByZWRpY3RvciB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuCmkuIFVzZSBnZ3BhaXJzKCkgdG8gaW52ZXN0aWdhdGUgY29ycmVsYXRpb25zIGJldHdlZW4gbWVkaWFuX2hvdXNlX3ZhbHVlIGFuZCB0aGUgcHJlZGljdG9ycyAodGhpcyBtYXkgdGFrZSBhIHdoaWxlIHRvIHJ1biwgZG9u4oCZdCB3b3JyeSwgbWFrZSBjb2ZmZWUgb3Igc29tZXRoaW5nKS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KR0dhbGx5OjpnZ3BhaXJzKGhvdXNpbmdfcHJpY2VzKQpgYGAKCkZyb20gdGhlc2UsIGl0IGxvb2tzIGxpa2UgdGhlcmUgYXJlIG90aGVyZXhwbGFuYXRvcnkgdmFyaWFibGVzIHRoYXQgYXJlIHN0cm9uZ2x5IHJlbGF0ZWQ6CgoqIHBvcHVsYXRpb24sIGhvdXNlaG9sZCBhbmQgdG90YWxfcm9vbXMgYXJlIGFsbCBjb3JyZWxhdGVkIC0gdGhleSBjb3VsZCBiZSBjb21iaW5lZCB0byBjcmVhdGUgYSBkZXJpdmVkIHZhcmlhYmxlIGFzIHNvbWUga2luZCBvZiBpbmRpY2F0b3Igb2YgaG91c2luZyBzdXBwbHkvZGVtYW5kLCBlLmcuIGhvdXNlaG9sZHMgKiB0b3RhbF9yb29tcyAvIHBvcHVsYXRpb24gKHdoZXJlID4gMSBpcyBwbGVudHkgb2Ygc3VwcGx5LCA8IDEgaXMgbm90IGVub3VnaCBzdXBwbHksIHNvIHNvbWUgZGVtYW5kIHByZXNzdXJlKQoqIGxvbmcgYW5kIGxhdCBhcmUgdHdvIHBhcnRzIHRvIGEgbG9jYXRpb24sIGFuZCBhcmUgc3Ryb25nbHkgcmVsYXRlZCAtIHRoZXkgc2hvdWxkIGJlIGNvbWJpbmVkIHRvIGluZGljYXRlIGluZGl2aWR1YWwgbG9jYXRpb25zIChvciBhcmVhcykgaW4gQ2FsaWZvcm5pYSAod2hlcmUgdGhpcyBkYXRhIGlzIGFib3V0KQoKVGFraW5nIHRoZXNlIGludG8gYWNjb3VudCwgYW5kIGxvb2tpbmcgYXQgdGhlIG1hZ25pdHVkZSBvZiB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIHRoYXQgYXJlIHNpZ25pZmljYW50ICgqcyksIEkgd291bGQgY29uc2lkZXIgaW5jbHVkaW5nIHRoZSBmb2xsb3dpbmcgaW4gYSBtb2RlbCwgaW4gb3JkZXIgb2Ygc3RlcHM6CgoxLiBpbmNvbWUKMi4gbG9jYXRpb24gKGxhdCwgbG9uZykgLSBsb29rIGF0IGxhdCBiZWxvdwozLiBkZW1hbmQgKHNvbWV0aGluZyBkZXJpdmVkIGZyb20gcG9wLCBob3VzZWhvbGQsIHJvb21zKSAtIGxvb2sgYXQgcm9vbXMgYmVsb3cKICAtIG5vdGU6IHJvb21zIGlzIGFsc28gYW4gaW5kaWNhdG9yIG9mIGhvaXVzZSBzaXplLCBjb3VsZCBiZSB1c2VmdWwgb24gaXRzIG93bgo0LiBhZ2UKCj4gaWkuIFBlcmZvcm0gZnVydGhlciBnZ3Bsb3QgdmlzdWFsaXNhdGlvbnMgb2YgYW55IHNpZ25pZmljYW50IGNvcnJlbGF0aW9ucyB5b3UgZmluZC4KCmBgYHtyfQojIGluY29tZQpob3VzaW5nX3ByaWNlcyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbWVkaWFuX2luY29tZSwgeSA9IG1lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX3BvaW50KGZpbGwgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKQpgYGAKCmBgYHtyfQpjb3IoaG91c2luZ19wcmljZXMkbWVkaWFuX2hvdXNlX3ZhbHVlLCBob3VzaW5nX3ByaWNlcyRtZWRpYW5faW5jb21lKQpgYGAKCioqSW50ZXJwcmV0OioqIFRoZXJlIGlzIGEgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gbWVkaWFuIGhvdXNlIHZhbHVlIGFuZCBtZWRpYW4gaW5jb21lIG9mIGFuIGFyZWEuIDY0JSBvZiB2YXJpYXRpb24gaW4gaG91c2UgdmFsdWUgaXMgZXhwbGFpbmVkIGJ5IHZhcmlhdGlvbiBpbiBpbmNvbWUuCgpUaGlzIGNvdWxkIGJlIGEgdXNlZnVsIGV4cGxhbmF0b3J5IHZhcmlhYmxlLgoKTm90ZSBhbHNvIHRoZXJlIHNlZW1zIHRvIGJlIGFuIHVwcGVyIGxpbWl0IGluIG1lZGlhbl9ob3VzZV92YWx1ZSwgb2RkIGxhY2sgb2YgdmFsdWVzIGFib3ZlIDUwMCwwMDAuCgoKYGBge3J9CiMgbGF0aXR1ZGUKaG91c2luZ19wcmljZXMgJT4lIAogIGdncGxvdChhZXMoeCA9IGxhdGl0dWRlLCB5ID0gbWVkaWFuX2hvdXNlX3ZhbHVlKSkgKwogIGdlb21fcG9pbnQoZmlsbCA9ICJncmV5NjAiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pCmBgYAoKVGhpcyBpcyBub3QgYSBsaW5lYXIgcmVsYXRpb25zaGlwIC0gdGhlciBhcHBlYXIgdG8gYmUgY2x1c3RlcnMgd2hlcmUgaG91c2UgcHJpY2UgaXMgbW9yZSB2YXJpYWJsZSAoaGlnaGVyIHBlYWtzKSAtIGkuZS4gY2VydGFpbiBsYXRpdHVkZXMsIEkgc3VzcGVjdCB0aGVzZSBhcmUgd2hlcmUgY2l0aWVzIGFyZS4KCkRvIG5vdCB1c2UgdGhpcyBhcyBhIHZhcmlhYmxlIG9uIGl0cyBvd24gKGNvbnNpZGVyIHVzaW5nIGxvY2F0aW9uIHRob3VnaCAtIG1heWJlIGV2ZW4gZW5jb2RpbmcgYXMgZGlmZmVyZW50IGFyZWFzPykKCmBgYHtyfQojIHRvdGFsX3Jvb21zCmhvdXNpbmdfcHJpY2VzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB0b3RhbF9yb29tcywgeSA9IG1lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX3BvaW50KGZpbGwgPSAiZ3JleTYwIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKQpgYGAKClRoaXMgZG9lcyBub3QgbG9vayBob21vZXNrZWRhc3RpYywgbG90cyBvZiBkYXRhIHBvaW50cyBhdCBsb3dlciB0b3RhbF9yb29tcywgYnV0IHdpdGggaGlnaCBkZWdyZWUgb2Ygc3ByZWFkIG9mIGhvdXNlIHZhbHVlcy4gU3VnZ2VzdCB0b3RhbF9yb29tcyBtaWdodCBuZWVkIHRvIGJlIHN0YW5kYXJkaXNlZCBieSBzb21lIGZhY3Rvcj8KCmBgYHtyfQojIG1lZGlhbl9hZ2UKaG91c2luZ19wcmljZXMgJT4lIAogIGdncGxvdChhZXMoeCA9IGhvdXNpbmdfbWVkaWFuX2FnZSwgeSA9IG1lZGlhbl9ob3VzZV92YWx1ZSkpICsKICBnZW9tX3BvaW50KGZpbGwgPSAiZ3JleTYwIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKQpgYGAKCk5vIG9idmlvdXMgY29ycmVsYXRpb24gaGVyZS4gRG8gbm90IHVzZSBpbiBtb2RlbC4KCiMgNSBleHBsb3JlIGNhdGVnb3JpY2FsIHByZWRpY3RvcgoKPiBTaG9ydGx5IHdlIG1heSB0cnkgYSByZWdyZXNzaW9uIG1vZGVsIHRvIGZpdCB0aGUgY2F0ZWdvcmljYWwgcHJlZGljdG9yIG9jZWFuX3Byb3hpbWl0eS4gSW52ZXN0aWdhdGUgdGhlIGxldmVsIG9mIG9jZWFuX3Byb3hpbWl0eSBwcmVkaWN0b3JzLiBIb3cgbWFueSBkdW1teSB2YXJpYWJsZXMgZG8geW91IGV4cGVjdCB0byBnZXQgZnJvbSBpdD8KCmBgYHtyfQpob3VzaW5nX3ByaWNlcyAlPiUgCiAgZGlzdGluY3Qob2NlYW5fcHJveGltaXR5KQpgYGAKClRoZXJlIGFyZSA1IGRpc3RpbmN0IGNhdGVnb3JpZXMgaW4gb2NlYW5fcHJveGltaXR5LCBzbyBleHBlY3QgdG8gZ2VuZXJhdGUgNCBkdW1teSB2YXJpYWJsZXMuICoqU2V0IGlubGFuZCBhcyB0aGUgcmVmZXJlbmNlKiosIGV2ZXJ5dGhpbmcgZWxzZSBnZXRzIG1vcmUgcHJveGltYWwgdG8gb2NlYW4uCgpgYGB7cn0KaG91c2luZ19wcmljZXNfdHJpbSA8LSBob3VzaW5nX3ByaWNlcyAlPiUgCiAgIyBzZXQgdXAgZHVtbXkgdmFyaWFibGVzIGZvciBvY2Vhbl9wcm94aW1pdHkKICBmYXN0RHVtbWllczo6ZHVtbXlfY29scyhzZWxlY3RfY29sdW1ucyA9ICJvY2Vhbl9wcm94aW1pdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZV9zZWxlY3RlZF9jb2x1bW5zID0gVFJVRSkgJT4lIAogIGphbml0b3I6OmNsZWFuX25hbWVzKCkgJT4lIAogICMgc2V0IGlubGFuZCBhcyByZWZlcmVuY2UKICBzZWxlY3QoLW9jZWFuX3Byb3hpbWl0eV9pbmxhbmQpCmBgYAoKYGBge3J9CiMgY2hlY2sgZm9yIGFueSBwb3RlbnRpYWwgY29ycmVsYXRpb25zCmhvdXNpbmdfcHJpY2VzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBvY2Vhbl9wcm94aW1pdHksIHkgPSBtZWRpYW5faG91c2VfdmFsdWUpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpIb3VzZSBwcmljZXMgb24gaXNsYW5kIGxvb2sgaGlnaGVyIHRoYW4gdGhlIHJlc3QsIHRoaXMgbWF5IGJlIGEgc2Vuc2libGUgcHJlZGljdG9yIHRvIHVzZSBpbiBhIG1vZGVsLgoKCioqVXBkYXRlOioqIGZyb20gYm94cGxvdCwgYWN0dWFsbHkgbG9va3MgbGlrZSAzIGNhdGVnb3JpZXMgdGhhdCBhcmUgc2ltaWxhcjogaW5sYW5kLCBpc2xhbmQsIGFuZCBwcm94aW1hbCB0byBvY2VhbiAoY29tYmluaW5nIHRoZSAzIG90aGVycykgLS0+IHdoaWNoIHdvdWxkIHN1Z2dlc3QgMiBkdW1teSB2YXJpYWJsZXMgKHdpdGggaW5sYW5kIHNldCBhcyByZWZlcmVuY2UpLgoKYGBge3J9CmhvdXNpbmdfcHJpY2VzICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ieSA9IG9jZWFuX3Byb3hpbWl0eSkKYGBgCgp+MjAsMDAwIG9ic2VydmF0aW9ucyBvZiB3aGljaCwgOCw2MDAgYXJlIDwxaCB0byBvY2VhbiwgNSBhcmUgb24gaXNsYW5kLCAyMDAwIGFyZSBuZWFyIGJheSwgMjQ0NiBhcmUgbmVhciBvY2VhbiwgcmVzdCBhcmUgaW5sYW5kICh+Niw1MDApLgoKVGhlcmUgYXJlIG9ubHkgNSBvYnNlcnZhdGlvbnMgZm9yIGlzbGFuZCwgc28gbm90IGVub3VnaCBkYXRhIGhlcmUgdG8gbW9kZWwgdGhpcyBjYXRlZ29yeSwgYW5kIGFsc28gZHJhZ2dpbmcgdXAgaG91c2UgcHJpY2VzIC0gc3VnZ2VzdCBkcm9wcGluZyB0aGlzIGNhdGVnb3J5IGFsdG9nZXRoZXIgZm9yIG5vdy4KClNvIGxldCdzIHVzZSBpbmxhbmQgYXMgcmVmZXJlbmNlLCBkcm9wIGlzbGFuZCBvYnNlcnZhdGlvbnMsIGFuZCBjb21iaW5lIHRoZSByZXN0IGludG8gYSBwcmVkaWN0b3IgIm9jZWFuX3Byb3hpbWFsIjoKCmBgYHtyfQpob3VzaW5nX3ByaWNlc19vY2VhbiA8LSBob3VzaW5nX3ByaWNlcyAlPiUgCiAgZmlsdGVyKG9jZWFuX3Byb3hpbWl0eSAhPSAiSVNMQU5EIikgJT4lIAogIG11dGF0ZShvY2Vhbl9wcm94aW1hbCA9IGlmX2Vsc2UoCiAgICBvY2Vhbl9wcm94aW1pdHkgPT0gIklOTEFORCIsCiAgICBGQUxTRSwgVFJVRSkpICU+JSAKICBzZWxlY3QoLW9jZWFuX3Byb3hpbWl0eSkKCmhlYWQoaG91c2luZ19wcmljZXNfb2NlYW4pCmBgYAoKIyA2IHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiB3aXRoIGluY29tZQoKPiBTdGFydCB3aXRoIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbi4gUmVncmVzcyBtZWRpYW5faG91c2VfdmFsdWUgb24gbWVkaWFuX2luY29tZSBhbmQgY2hlY2sgdGhlIHJlZ3Jlc3Npb24gZGlhZ25vc3RpY3MuCgpgYGB7cn0KbW9kZWxfaW5jb21lIDwtIGxtKGZvcm11bGEgPSBtZWRpYW5faG91c2VfdmFsdWUgfiBtZWRpYW5faW5jb21lLCBob3VzaW5nX3ByaWNlc190cmltKQpgYGAKCmBgYHtyfQojIGFzIGFib3ZlCm1vc2FpYzo6cGxvdE1vZGVsKG1vZGVsX2luY29tZSkKYGBgCgpgYGB7cn0KbGlicmFyeShnZ2ZvcnRpZnkpCmF1dG9wbG90KG1vZGVsX2luY29tZSkKYGBgCgpJbnRyZXByZXQ6CgoqIFJ2RiBwbG90IGhhcyBhIGJpZyB3ZWRnZSBzaGFwZSwgd2hpY2ggc3VnZ2VzdHMgaXQgaXMgaGV0ZXJza2VkYXN0aWMgLSB3ZSBuZWVkIHRvIGFkZCBhbm90aGVyIHZhcmlhYmxlIHRvIGV4cGxhaW4gdGhlIHZhcmlhdGlvbiBtb3JlIGNvbnNpc3RlbnRseSBhY3Jvc3MgeCB2YWx1ZXMgYW5kIGdlbmVyYXRlIGEgZ29vZCBsaW5lYXIgbW9kZWwgaGVyZQoqIFFRIHBsb3QgaXMgbm90IGxpbmVhciwgdGhlIHJlc2lkdWFscyBhcmUgbm90IG5vcm1hbGx5IGRpc3RydWJ0ZWQsIHdvcnNlIGF0IHRoZSBleHRyZW1lcywgbG9vayBuZWdhdGl2ZWx5IHNrZXdlZCAocmlnaHQgc2tld2VkKSBpbiB0aGUgaGlzdG9ncmFtIGJlbG93CiogU2NhbGUtTG9jYXRpb24gLSBsb29rcyB0byBiZSBzb21lIHBvdGVudGlhbCBvdXRsaWVycywgcG9pbnRzIDExMzMxIGFuZCAxNzU1Lj8uIChjYW5ub3QgZXhwYW5kIHBsb3QgdG8gc2VlKQoqIFRoZXNlIHBvaW50cyBhbmQgYW5vdGhlciBwb2ludCAoMTU2NS4uPykgaGF2ZSBoaWdoIGxldmVyYWdlIGFuZCBsb29rIHRvIGJlIGluZmx1ZW5jaW5nIGluIHRoZSBSdkwgcGxvdAoKYGBge3J9CiMgaW5zcGVjdCBkaXN0cmlidXRpb24gb2YgdGhlIHJlc2lkdWFscwpoaXN0KG1vZGVsX2luY29tZSRyZXNpZHVhbHMpCmBgYAoKYGBge3J9CiMgaW5zcGVjdCB0aGUgaGlnaCBsZXZlcmFnZSAvIGluZmx1ZW5jaW5nIHBvaW50cwpob3VzaW5nX3ByaWNlc190cmltICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBtZWRpYW5faW5jb21lLCB5ID0gbWVkaWFuX2hvdXNlX3ZhbHVlKSkgKwogIGdlb21fcG9pbnQoZmlsbCA9ICJncmV5NjAiLCBzaXplID0gMC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gMTpucm93KGhvdXNpbmdfcHJpY2VzKSksIG51ZGdlX3ggPSAwLjUsIG51ZGdlX3kgPSAxLCBzaXplID0gMikKYGBgCgpQb2ludHMgMTEzMzEsIDE3NTU2IGFuZCAxNTU1IGFwcGVhciB0byBiZSB0aGUgcG9pbnRzIG9mIGludGVyZXN0LgoKYGBge3J9CmhvdXNpbmdfcHJpY2VzW2MoMTEzMzEsMTc1NTYsMTU1NSksXQpgYGAKClJhdyBkYXRhIGRvZXNuJ3QgbG9vayBzdXNwZWN0LCBub3QgYW4gb2J2aW91cyBwcm9jZXNzaW5nIGVycm9yLiBCdXQgbm90ZSB0aGUgcG9wdWxhdGlvbiBsb29rcyB0aW55LCB2ZXJ5IGZldyBob3VzZWhvbGRzLiBNaWdodCB3YW50IHRvIGNvbnNpZGVyIGZpbHRlcmluZyBmb3IgZGF0YSB3aGVyZSBob3VzZWhvbGRzIGlzIHNpemVhYmxlIGVub3VnaC4uLgoKYGBge3J9CmhvdXNpbmdfcHJpY2VzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwb3B1bGF0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDAsIGNvbG91ciA9ICJ3aGl0ZSIpCgpob3VzaW5nX3ByaWNlcyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaG91c2Vob2xkcykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwLCBjb2xvdXIgPSAid2hpdGUiKQpgYGAKCkl0IGRvZXNuJ3QgbWFrZSBzZW5zZSB0byBmaWx0ZXIgb3V0IGxvdyBudW1iZXIgaG91c2Vob2xkcyBvciBwb3B1bGF0aW9uLCBnaXZlbiB0aGUgZGlzdHJpYnV0aW9ucyBoZXJlIC0gdGhleSBkb24ndCBzZWVtIHRvIGJlIG9kZC4KClRyeSByZW1vdmluZyBhIGNvdXBsZSBvZiBwb2ludHM6CgpgYGB7cn0KIyByZW1vdmUgdGhlIHR3byBtb3N0IGNvbmNlcm5pbmcgcG9pbnRzCmhvdXNpbmdfcHJpY2VzX3JtIDwtIGhvdXNpbmdfcHJpY2VzX3RyaW0gJT4lIAogIHNsaWNlKC1jKDExMzMxLDE3NTU2KSkKCm1vZGVsX2luY29tZV9ybSA8LSBsbShtZWRpYW5faG91c2VfdmFsdWUgfiBtZWRpYW5faW5jb21lLCBob3VzaW5nX3ByaWNlc19ybSkKCmF1dG9wbG90KG1vZGVsX2luY29tZV9ybSkKYGBgCgpSZW1vdmluZyB0aGVzZSB2YWx1ZXMgaGFzIG5vdCBoZWxwZWQgdGhlIG1vZGVsLCBpdCBqdXN0IHNob3dzIGV2ZW4gbW9yZSBleHRyZW1lIHZhbHVlcyAoaGlnaCBpbmNvbWUsIGxvdyBob3VzZSBwcmljZSkuIFNvIGRvIG5vdCByZW1vdmUgdGhlc2UgcG9pbnRzIChjb250aW51ZSB3aXRoIGBob3VzaW5nX3ByaWNlc190cmltYCkuCgpgYGB7cn0Kc3VtbWFyeShtb2RlbF9pbmNvbWUpCmBgYAoKSGVyZSwgdGhlIG1vZGVsIHN1Z2dlc3RzIGEgZnVuY3Rpb246CgptZWRpYW5faG91c2VfdmFsdWUgKGluICQpIH49IH40MCwwMDAqbWVkaWFuX2luY29tZSArIDQ1LDUwMAoKdGhlIHAtdmFsdWUgKDwyLjJlLTE2KSBpcyB2ZXJ5IHNtYWxsIChtdWNoIGJlbG93IGEgMC4wNSBzaWduaWZpY2FuY2UgdGhyZXNob2xkKSwgd2hpY2ggaW5kaWNhdGVzIGl0IGlzIGhpZ2hseSB1bmxpa2VseSB0aGlzIG5vbi16ZXJvIGNvZWZmaWNpZW50IHdvdWxkIGhhcHBlbiBpbiBhIHdvcmxkIHdoZXJlIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZSAodGhhdCBtZWRpYW5faW5jb21lIGhhcyBubyBhZmZlY3Qgb24gbWVkaWFuX2hvdXNlX3ZhbHVlKSwgc28gd2UgY2FuIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGFuZCBzYXkgdGhlcmUgaXMgZW5vdWdoIGV2aWRlbmNlIHRvIHN1Z2dlc3QgdGhhdCBpbmNvbWUgZG9lcyBhZmZlY3QgaG91c2UgdmFsdWUuCgpUaGUgc3RhbmRhcmQgZXJyb3IgaXMgc2l6ZWFibGUgKH5VU0QgNzUsMDAwKSAtIHdlIGFyZSB0YWxraW5nIGFib3V0IGhvdXNlIHByaWNlcyBpbiB0aGUgcmVnaW9uIG9mIFVTRCAzMDAtNTAwLDAwMCB3aXRoIGEgYW4gSVFSIG9mIH4gMTMwICh0aGUgUVEgcmFuZ2UgaXMgfiAxMjAtMjUwayBpbiB0aGUgYm94cGxvdCBiZWxvdykgc28gdGhpcyBlcnJvciBpcyBoYWxmIHRoZSBJUVIgc28gaXMgcXVpdGUgbGFyZ2UgZm9yIHRoaXMgdG8gYmUgYSBnb29kIG1vZGVsLgoKYGBge3J9CmhvdXNpbmdfcHJpY2VzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpUaGUgUjIgaXMgMC40MTI5LCB3aGljaCBpcyBhbiBvayBjb3JyZWxhdGlvbiBmb3Igc3VjaCBhIHZhcmlhYmxlIG91dGNvbWUgKGhvdXNlIHZhbHVlKS4KCioqT3ZlcmFsbCoqIHRoaXMgc2ltcGxlIGxpbmVhciBtb2RlbCBkb2VzIG5vdCBtZWV0IHNldmVyYWwgYXNzdW1wdGlvbnMgb2YgbGluZWFyIHJlZ3Jlc3Npb24sIHNvIHdlIHNob3VsZCBub3QgYWNjZXB0IHRoYXQgdGhpcyBtb2RlbCBpcyBhIGdvb2QgZml0IGZvciBvdXIgZGF0YS4gV2UgbmVlZCB0byBpbXByb3ZlIGl0IC0gd2UgY2FuIHRyeSBhZGRpbmcgYW5vdGhlciBwcmVkaWN0b3IgdmFyaWFibGUgaW4uLi4KCiMgNyBhZGQgYW5vdGhlciB2YXIgdG8gbW9kZWwKCj4gQWRkIGFub3RoZXIgcHJlZGljdG9yIG9mIHlvdXIgY2hvaWNlLiBDaGVjayB5b3VyIGFzc3VtcHRpb25zLCBkaWFnbm9zdGljcywgYW5kIGludGVycHJldCB0aGUgbW9kZWwuCgojIyA3YSArIGlzbGFuZAoKTGV0J3MgdHJ5IHdpdGggb2NlYW4gcHJveGltaXR5OiBpc2xhbmQuIAoKQ29uc2lkZXJpbmcgaXQgYXMgYW4gaW5kZXBlbmRlbnQgdmFyaWFibGUsIGJ1dCBub3QgdXNpbmcgdGhlIG90aGVyIG9jZWFuX3Byb3hpbWl0aWVzIC0tIG5vdCBzdXJlIGlmIHRoaXMgaXMgYSBjb3JyZWN0IGFwcHJvYWNoIGZvciBub24tYmluYXJ5IGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4uLgoKYGBge3J9Cm1vZGVsX2luY29tZV9pc2xhbmQgPC0gbG0obWVkaWFuX2hvdXNlX3ZhbHVlIH4gbWVkaWFuX2luY29tZSArIG9jZWFuX3Byb3hpbWl0eV9pc2xhbmQsIGhvdXNpbmdfcHJpY2VzX3RyaW0pCmBgYAoKYGBge3J9CmF1dG9wbG90KG1vZGVsX2luY29tZV9pc2xhbmQpCmBgYAoKT2ggdGhpcyBoYXMgbm90IGltcHJvdmVkIHRoZSBtb2RlbC4uLiAKCiogUnZGIC0gc3RpbGwgZ290IGEgd2VkZ2Ugc2hhcGUsIHNvIG5vdCBsaW5lYXIKKiBRUSAtIHJlc2lkdWFscyBzdGlsbCBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKKiBTLUwgc3RpbGwgZ290IGEgd2VkZ2UgaGVyZSB0b28sIHNvIGhldGVyc2tlZGFzdGljCiogUnZMIC0gc2hvd3MgbWFueSBwb2ludHMgYXJlIG5lYXIgdGhlIGNlbnRyb2lkLCBhIGNvdXBsZSBvZiBwb2ludHMgYXJlIHZlcnkgZmFyCgpgYGB7cn0KaGlzdChtb2RlbF9pbmNvbWVfaXNsYW5kJHJlc2lkdWFscykKYGBgCgpUaGlzIGRpc3RyaWJ1dGlvbiBkb2Vzbid0IGxvb2sgc28gYmFkIHRob3VnaCAtIG1heWJlIGl0IGlzIHNoaWZ0ZWQgbGVmdD8KCmBgYHtyfQpzdW1tYXJ5KG1vZGVsX2luY29tZV9pc2xhbmQpCmBgYAoKYGBge3J9Cm1vc2FpYzo6cGxvdE1vZGVsKG1vZGVsX2luY29tZV9pc2xhbmQpCmBgYAoKSSB3b3VsZCBleHBlY3QgdG8gaGF2ZSBzZWVuIHBhcmFsbGVsIHNsb3BlcyBoZXJlIChvbmUgZm9yIGlzbGFuZCA9IFRSVUUsIGFub3RoZXIgZm9yIGlzbGFuZCA9IEZBTFNFKSAtIGJ1dCB0aGVyZSBpcyBvbmx5IG9uZSBsaW5lLgoKYGBge3J9CmhvdXNpbmdfcHJpY2VzX3RyaW0gJT4lIAogIGZpbHRlcihvY2Vhbl9wcm94aW1pdHlfaXNsYW5kID09IDEpCmBgYApUaGVyZSBhcmUgb25seSA1IG9ic2VydmF0aW9ucyB3aXRoIGlzbGFuZCA9PSBUUlVFIHNvIGNhbm5vdCB1c2UgdGhpcyBpbiBvdXIgbW9kZWwhCgpUcnkgYWdhaW4uLi4KCiMjIDdiICsgaG91c2Vfc2l6ZSAvIG9jY3VwYW5jeQoKTGV0J3MgZGVyaXZlIG1lYW4gaG91c2Vfc2l6ZSB1c2luZyB0b3RhbF9yb29tcy9ob3VzZWhvbGRzCgpgYGB7cn0KaG91c2luZ19wcmljZXNfdHJpbSA8LSBob3VzaW5nX3ByaWNlc190cmltICU+JSAKICBtdXRhdGUobWVhbl9ob3VzZV9zaXplID0gdG90YWxfcm9vbXMgLyBob3VzZWhvbGRzKQoKaGVhZChob3VzaW5nX3ByaWNlc190cmltKQpgYGAKCmBgYHtyfQpob3VzaW5nX3ByaWNlc190cmltICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBtZWFuX2hvdXNlX3NpemUsIHkgPSBtZWRpYW5faG91c2VfdmFsdWUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41KQpgYGAKClRoaXMgaXMgYSBuby1nb2VyIC0gbG9va3MgbGlrZSBtYWpvcml0eSBvZiBob3VzZXMgYXJlIGFyb3VuZCB0aGUgc2FtZSBzaXplLCB3aXRoIGEgZmV3IGV4dHJlbWUgdmFsdWVzLi4uCgpXaGF0IGFib3V0IGRlbWFuZCwgc29tZSBraW5kIG9mIG1lYXN1cmUgb2YgaG91c2Vob2xkcyBieSBwb3B1bGF0aW9uPyBEZXJpdmUgYXZnX29jY3VwYW5jeQoKYGBge3J9CmhvdXNpbmdfcHJpY2VzX3RyaW0gPC0gaG91c2luZ19wcmljZXNfdHJpbSAlPiUgCiAgc2VsZWN0KC1tZWFuX2hvdXNlX3NpemUpICU+JSAKICBtdXRhdGUoYXZnX29jY3VwYW5jeSA9IHBvcHVsYXRpb24gLyBob3VzZWhvbGRzKSAjIGluZGljYXRlcyAjIHBlb3BsZSBwZXIgaG91c2Vob2xkCgpoZWFkKGhvdXNpbmdfcHJpY2VzX3RyaW0pCmBgYAoKYGBge3J9CmhvdXNpbmdfcHJpY2VzX3RyaW0gJT4lIAogIGdncGxvdChhZXMoeCA9IGF2Z19vY2N1cGFuY3ksIHkgPSBtZWRpYW5faG91c2VfdmFsdWUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41KQpgYGAKCk5vdyB0aGVyZSBhcmUgNyBwb2ludHMgdGhhdCBsb29rIGxpa2UgZXh0cmVtZSB2YWx1ZXMuLi4gY291bGQgd2UgZmlsdGVyIGZvciBhdmdfb2NjdXBhbmN5IDwgMTAwPz8KClRoaXMgbWF5IGJlIG92ZXItd3JhbmdsaW5nIHRoZSBkYXRhLCBidXQgdGhlc2UgZmV3IHZhbHVlcyBkbyBub3QgbWFrZSBzZW5zZSBmb3IgYSBtb2RlbCBsb29raW5nIGF0IH4yMCwwMDAgb2JzZXJ2YXRpb25zCgpgYGB7cn0KbnJvdyhob3VzaW5nX3ByaWNlc190cmltKQpgYGAKCmBgYHtyfQojID4gMTAwIGZpbHRlcnMgNCBvdXQKIyA+IDUwIGZpbHRlcnMgNyBvdXQKaG91c2luZ19wcmljZXNfcm1faGlnaF9vY2N1cGFuY3kgPC0gaG91c2luZ19wcmljZXNfdHJpbSAlPiUgCiAgZmlsdGVyKCFhdmdfb2NjdXBhbmN5ID4gNTApCmBgYAoKYGBge3J9CmhvdXNpbmdfcHJpY2VzX3JtX2hpZ2hfb2NjdXBhbmN5ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBhdmdfb2NjdXBhbmN5LCB5ID0gbWVkaWFuX2hvdXNlX3ZhbHVlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkKYGBgCgpOb3cgd2UgY2FuIHNlZSBzY2F0dGVyIG9mIGRhdGEsIGJ1dCBzdGlsbCBkb2VzIG5vdCBsb29rIHRvIGhhdmUgYW55IGxpbmVhciByZWxhdGlvbnNoaXAuIENvdWxkIHRyeSBzY2FsaW5nIGJ1dCBJIGRvbid0IHRoaW5rIHRoYXQgaXMgdGhlIHByb2JsZW0gaGVyZS4uLgoKIyMgN2MgKyBvY2Vhbl9wcm94aW1pdHkKClVzaW5nIGBob3VzZV9wcmljZXNfb2NlYW5gIGZvciBvdXIgZHVtbXkgdmFyaWFibGUgb2NlYW5fcHJveGltYWw6CgpgYGB7cn0KaG91c2luZ19wcmljZXNfb2NlYW4gJT4lIAogIGdncGxvdChhZXMoeCA9IG9jZWFuX3Byb3hpbWFsLCB5ID0gbWVkaWFuX2hvdXNlX3ZhbHVlKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKVGhlcmUgbG9va3MgdG8gYmUgYW4gZWZmZWN0IG9mIG9jZWFuIHByb3hpbWl0eSB3aXRoIGhvdXNlIHByaWNlcywgd2hlcmUgYmVpbmcgY2xvc2UgdG8gdGhlIG9jZWFuIGhhcyBoaWdoZXIgaG91c2UgcHJpY2VzLCBidXQgdGhlcmUncyBhbHNvIGEgbG9uZyB0YWlsIG9mIHZhbHVlcyBmb3IgRkFMU0UuIExldCdzIHRyeSBpbmNsdWRpbmcgdGhpcyBpbiBvdXIgbW9kZWwgYXMgYSBzZWNvbmQgZXhwbGFuYXRvcnkgdmFyaWFibGU6CgpgYGB7cn0KbW9kZWxfaW5jb21lX29jZWFuIDwtIGxtKG1lZGlhbl9ob3VzZV92YWx1ZSB+IG1lZGlhbl9pbmNvbWUgKyBvY2Vhbl9wcm94aW1hbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBob3VzaW5nX3ByaWNlc19vY2VhbikKYGBgCgpgYGB7cn0KbW9zYWljOjpwbG90TW9kZWwobW9kZWxfaW5jb21lX29jZWFuKQpgYGAKClRoaXMgZ2l2ZXMgdXMgMiBwYXJhbGxlbCBsaW5lcyAtIGl0IGxvb2tzIGFzIHRob3VnaCBiZWluZyBjbG9zZSB0byB0aGUgb2NlYW4gc2hpZnRzIG1lZGlhbiBob3VzZV92YWx1ZSBoaWdoZXIuCgpMZXQncyBkaWFnbm9zZSB0aGlzIG1vZGVsOgoKYGBge3J9CmF1dG9wbG90KG1vZGVsX2luY29tZV9vY2VhbikKYGBgCgoxLiBSdkYgLSBzdGlsbCBhIHdlZGdlIHNoYXBlLCBzbyBub3QgbGluZWFyCjIuIFFRIHBsb3QgZG9lcyBub3Qgc3VnZ2VzdCBub3JtYWxseSBkaXN0cmlidXRlZCBhdCB0aGUgZXh0cmVtZXMKMy4gUy1MIC0gc3RpbGwgYSB3ZWRnZSBzaGFwZSBzbyBoZXRlcnNrZWRhc3RpYwo0LiB0aGVyZSBtYXkgc3RpbGwgYmUgYSBjb3VwbGUgb2YgcG9pbnRzIHRoYXQgYXJlIGluZmx1ZW5jaW5nIHRoZSBtb2RlbCBoZXJlCgpTbyB0aGUgbW9kZWwgZG9lcyBub3QgbWF0Y2ggb3VyIGFzc3VtcHRpb25zIG9mIGxpbmVhciByZWdyZXNzaW9uLgoKTGV0J3MgbG9vayBhdCB0aGUgbW9kZWwgYW55d2F5Li4uIAoKYGBge3J9CnN1bW1hcnkobW9kZWxfaW5jb21lX29jZWFuKQpgYGAKClRoaXMgbW9kZWwgKGlmIGl0IHdlcmUgYSBnb29kIGZpdCkgc3VnZ2VzdHMgYmFzZSBtZWRpYW4gaG91c2UgdmFsdWUgaXMgVVNEIDEyLDAwMCBhbmQgaW5jcmVhc2VzIHdpdGggbWVkaWFuIGluY29tZSAobXVsdGlwbGllciBlZmZlY3QsICozNSwwMDApLCBhbmQgaXMgYWxzbyBzaGlmdGVkIHVwIGlmIHRoZSBsb2NhdGlvbiBpcyBjbG9zZSB0byB0aGUgb2NlYW4gKGJ5IFVTRCA3OCwwMDApLgoKVGhlIFIyIGluZGljYXRlcyB0aGUgbW9kZWwgaXMgYSBnb29kIGZpdCwgYW5kIHRoZSBwLXZhbHVlIGlzIHdheSBiZWxvdyBhIHNpZ25maWNhbmNlIHRocmVzaG9sZCBvZiAwLjA1LCBzdWdnZXN0aW5nIHdlIGNhbiByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyAodGhhdCB0aGVzZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgZG8gbm90IHByZWRpY3QgaG91c2UgdmFsdWUpLgoKVGhpcyBtb2RlbCBzdW1tYXJ5IHdvdWxkIGxvb2sgZ29vZCwgZXhjZXB0IHRoYXQgbG9va2luZyBhdCBhdXRvcGxvdCBpbmRpY2F0ZXMgdGhlIGxpbmVhciBtb2RlbCBpcyBub3QgYXBwcm9wcmlhdGUgaGVyZS4KClRvIHRyeTogc3RhbmRhcmRpc2Ugb3Igc2NhbGUgdGhlIHZhcmlhYmxlcyBzb21laG93PyBCdXQgdGhpcyB3b24ndCBjaGFuZ2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiBlYWNoIChpLmUuIHdvbid0IHNoaWZ0IHRoZSBsb25nIHRhaWwgb2YgaGlnaCBob3VzZSBwcmljZXMgb3IgaGlnaCBpbmNvbWUuLi4pCg==